<?php
namespace Zyt;
use \PDO;
use \PDOException;

/**
 * 中云通数据数据库通信 API
 * Author: Kenny.Hu
 * Date: 2016/2/1
 * Time: 11:11
 */
class Api extends Zyt
{
    // ----------------------------- 以下是与游戏相关的接口 ----------------------
    /**
     * 添加游戏
     *
     * @param $category    int    游戏分类ID
     * @param $name        string 游戏名称
     * @param $initials    string 游戏名称首字母
     * @param $uid         int    添加游戏用户ID
     * @param $recharge    int    充值类型 |1:只能通币充值 |2:所有适合
     * @param $is_recharge int    是否可充值
     * @param $currency    string 游戏币名称
     * @param $rate        string 游戏比率|1:100
     *
     * @return array
     */
    public function addGame($category, $name, $initials, $uid, $recharge, $is_recharge, $currency = null, $rate = null){
        $time = time();
        $currency = $currency && $rate ? $this->gameCurrencyRateTranslator($currency, $rate) : null;
        $params = array($category, $name, $initials, $uid, $recharge, $is_recharge, $uid, $time, $uid, $time, $currency);
        return $this->_call('PF_Game_AddGame', $params, 1);
    }

    /**
     * 更新游戏信息
     *
     * @param $id          int      游戏ID
     * @param $title       string   游戏名称
     * @param $initials    string   游戏索引
     * @param $recharge    int      充值类型
     * @param $is_recharge int      是否可充值
     * @param $category    int      游戏分类
     * @param $uid         int      更新用户的ID
     * @param $currency    string   游戏币名称
     * @param $rate        string   游戏比率|1:100
     *
     * @return array
     */
    public function updateGameInfo($id, $title, $initials, $recharge, $is_recharge, $category, $uid, $currency = null, $rate = null){
        $time = time();
        $currency = $currency && $rate ? $this->gameCurrencyRateTranslator($currency, $rate) : null;
        $params = array($id, $title, $initials, $recharge, $is_recharge, $category, $uid, $time, $currency);
        return $this->_call('PF_Game_UpdateGameInfo', $params, 1);
    }

    /**
     * @param $currency  string 游戏币名称
     * @param $rate      string 比率
     *
     * @return string|null
     */
    private function gameCurrencyRateTranslator($currency, $rate){
        $filter = str_replace([' ', '：'], ['', ':'], $rate);
        preg_match('/^[1-9]\d*:[1-9]\d*$/', $filter, $matches);
        if(empty($matches))   return null;                   //比率设置格式不正确
        return $currency . ',' . $matches[0];
    }

    /**
     * 根据游戏名称搜索游戏
     *
     * @param $keyword  string  关键字
     * @param $cid      int     分类ID
     *
     * @return array
     */
    public function searchGame($keyword, $cid){
        $params = array($keyword, $cid);
        return $this->_call('PF_Game_SearchGame', $params);
    }

    /**
     * 添加游戏分类
     *
     * @param $pid     int     父ID，顶级分类则可以定义为0
     * @param $name    string  分类名称
     * @param $uid     int     游戏名称
     * @param $sort    string  排序
     *
     * @return array
     */
    public function addGameCategory($pid, $name, $uid, $sort){
        $time = time();
        $params = array($pid, $name, $uid, $time, null, null, null, $sort);
        return $this->_call('PF_Game_AddGameClass', $params, 1);
    }

    /**
     * 根据分类ID删除游戏分类
     *
     * @param $cid    int   分类ID
     *
     * @return array
     */
    public function deleteCategoryById($cid){
        $params = array($cid);
        return $this->_call('PF_Game_DelCategoryById', $params, 1);
    }

    public function deleteGameById($gid){
        $params = array($gid);
        return $this->_call('PF_Game_DelGameById', $params, 1);
    }

    /**
     * 获取游戏列表
     *
     * @return array
     */
    public function getGameList(){
        return $this->_call('PF_Game_GetGame');
    }



    /**
     * 根据游戏ID获取游戏服务器
     *
     * @param $gid int 游戏ID
     *
     * @return array
     */
    public function getGameServer($gid){
        $params = array($gid);
        return $this->_call('PF_Game_GetGameServer', $params);
    }

    /**
     * 添加游戏区服
     *
     * @param $gid       int     游戏ID
     * @param $data      array   要插入的区服数据
     *
     * @return bool|int
     */
    public function addGameServer($gid, $data = array()){
        if((int)$gid <= 0 || empty($data) || !is_array($data))  return false;

        $sql = 'INSERT INTO sso_game_server (game_server_name, game_id) values';
        foreach( $data as $v ){
            $sql .= "('$v', $gid) ,";
        }
        $sql = substr($sql, 0, -1);
        $r = $this->query($sql);
        return $r->rowCount();
    }

    /**
     * 根据服务器ID获取游戏服务器信息
     *
     * @param $sid    int  服务器ID
     * @return array
     */
    public function getGameServerInfo($sid){
        $params = array($sid);
        return $this->_call('PF_Game_GetGameServerInfoById', $params);
    }

    public function dropServerByID($sid){
        $param = array($sid);
        return $this->_call('PF_Gaem_DropServerById', $param, 1);
    }

    /**
     * 获取所有游戏分类
     *
     * @return array
     */
    public function getGameCategory(){
        return $this->_call('PF_Game_GetGameClass');
    }

    /**
     * 根据分类ID更新分类信息
     *
     * @param $id     int     分类ID
     * @param $title  string  名称
     * @param $pid    int     父ID
     * @param $sort   int     排序
     * @param $uid    int     更新用户的ID
     *
     * @return array
     */
    public function updateGameCategory($id, $title, $pid, $sort, $uid){
        $time = time();
        $params = array($id, $title, $pid, $sort, $uid, $time);
        return $this->_call('PF_Game_UpdateCategoryInfo', $params, 1);
    }

    /**
     * 根据ID获取分类信息
     *
     * @param  $id    int   分类ID
     * @return array
     */
    public function getGameCategoryInfo($id){
        $param = array($id);
        return $this->_call('PF_Game_GetGameClassByID', $param);
    }

    /**
     * 获取游戏充值类型列表
     *
     * @return array
     */
    public function getGameRechargeList(){
        return $this->_call('PF_Game_GetRechargeType');
    }

    /**
     * 根据游戏ID获取该游戏的信息
     *
     * @param $gid int 游戏ID
     * @return array
     */
    public function getGameInfo($gid){
        $params = array($gid);
        return $this->_call('PF_Game_GetGameInfo', $params);
    }

    // ----------------------------- 以下是与用户资金相关的接口 ----------------------
    /**
     * 根据用户ID获取用户资金信息
     *
     * @param $uid int 用户ID
     * @return array
     */
    public function getUserCapital($uid){
        $params = array($uid);
        return $this->_call('PF_Capital_GetUserCapital', $params);
    }

    /**
     * 获取交易类型
     *
     * @return array
     */
    public function getTraderType(){
        return $this->_call('PF_Capital_GetTraderType');
    }

    /**
     * 添加交易明细记录
     *
     * @param $order_num      string  订单号
     * @param $out_user_id    int     支出用户ID
     * @param $in_user_id     int     收入用户ID
     * @param $platform_id    int     平台ID
     * @param $log_type       int     交易类型,可以先用getTraderType获取交易类型ID
     * @param $amount         float   交易数量，输入正数
     * @param $description    string  交易描述
     * @param null $game_id   int     交易游戏ID，可选
     * @param null $server_id int     可选
     * @return array
     */
    public function addCapitalLog($order_num, $out_user_id, $in_user_id, $platform_id, $log_type, $amount, $description, $game_id = null, $server_id = null){
        $time = time();
        $amount = abs($amount);
        $params = array($order_num, $out_user_id, $in_user_id, $platform_id, $log_type, $amount, $time, $description, $game_id, $server_id);
        return $this->_call('PF_Capital_AddMoneyLog', $params , 1);
    }

    /**
     * 根据用户ID获取交易明细记录
     *
     * @param $uid   int 用户ID
     * @return array
     */
    public function getCapitalLog($uid){
        $param = array($uid);
        return $this->_call('PF_Capital_GetMoneyLog', $param);
    }

    /**
     * 根据用户ID修改用户消费余额
     *
     * @param $uid    int   用户ID
     * @param $amount float 可以是正数或者负数
     * @return array
     */
    public function updateUserMoney($uid, $amount){
        $params = array($uid, $amount);
        return $this->_call('PF_Capital_UpdateConsumedByUserID', $params, 1);
    }

    /**
     * 根据用户ID修改用户冻结消费余额
     *
     * @param $uid    int   用户ID
     * @param $amount float 可以是正数或者负数
     * @return array
     */
    public function updateUserFznMoney($uid, $amount){
        $params = array($uid, $amount);
        return $this->_call('PF_Capital_UpdateFreezeConsumedByUserID', $params, 1);
    }

    /**
     * 根据用户ID修改用户通币
     *
     * @param $uid    int   用户ID
     * @param $amount float 可以是正数或者负数
     * @return array
     */
    public function updateUserTb($uid, $amount){
        $params = array($uid, $amount);
        return $this->_call('PF_Capital_UpdateTongByUserID', $params, 1);
    }

    /**
     * 根据用户ID修改用户冻结通币
     *
     * @param $uid    int   用户ID
     * @param $amount float 可以是正数或者负数
     * @return array
     */
    public function updateUserFznTb($uid, $amount){
        $params = array($uid, $amount);
        return $this->_call('PF_Capital_UpdateFreezeTongByUserID', $params, 1);
    }

    /**
     * 执行分红操作
     *
     * @param $sn   string   流水号
     * @return array
     */
    public function Bonus($sn){
        $params = array($sn, time());
        return $this->_call('PF_Capital_Bonus', $params, 1);
    }


    // ----------------------------- 以下是与用户信息相关的接口 ----------------------
    /**
     * 根据用户id获得用户信息
     *
     * @param $uid    int   用户ID
     * @return array
     */
    public function getUserInfo($uid){
        $params = array($uid);
        return $this->_call('PF_User_GetUserByUserID', $params);
    }

    /**
     * 添加用户
     *
     * @param $username     string   用户名
     * @param $password     string   登陆密码
     * @param $paypassword  string   支付密码
     * @param $mobile       string   手机号码
     * @param $email        string   E-mail地址
     * @param $ip           string   注册客户端IP
     *
     * @return array
     */
    public function addUser($username, $password, $paypassword, $mobile, $email, $ip){

        if(empty( $paypassword )) $paypassword = NULL;
        if(empty( $mobile )) $mobile = NULL;
        if(empty( $email )) $email = NULL;
        if(empty( $ip )) $ip = NULL;

        $params = array($username, $password, $paypassword, $mobile, $email, $ip);
        return $this->_call('PF_User_AddUser', $params, 1);
    }

    /**
     * 修改用户基本信息
     *
     * @param $uid          string   用户ID
     * @param $password     string   登陆密码, 密码不修改，传入NULL
     * @param $paypassword  string   支付密码, 密码不修改，传入NULL
     * @param $mobile       string   手机号码
     * @param $email        string   E-mail地址
     *
     * @return array
     */
    public function editUser($uid, $password, $paypassword, $mobile, $email){
        $params = array($uid, $password, $paypassword, $mobile, $email);
        return $this->_call('PF_User_EditUser', $params, 1);
    }


    /**
     * 根据用户名获得用户信息
     *
     * @param $name    string   用户名
     *
     * @return array
     */
    public function getUserByName($name){
        $params = array($name);
        return $this->_call('PF_User_GetUserByUserName', $params);
    }

    /**
     * 根据用户名ID 更新用户手机
     *
     * @param $uid    int    用户ID
     * @param $mobile string 手机号码
     * @return array
     */
    public function updateUserMobile($uid, $mobile){
        $params = array($uid, $mobile);
        return $this->_call('PF_User_UpdateMobileByUserID', $params, 1);
    }

    /**
     * 根据用户名ID 更新用户支付密码
     *
     * @param $uid        int      用户ID
     * @param $password   string   密码
     *
     * @return array
     */
    public function updateUserPayPassword($uid, $password){
        $params = array($uid, $password);
        return $this->_call('PF_User_UpdatePayPasswordByUserID', $params, 1);
    }

    /**
     * 根据用户名ID 更新用户 E-mail
     *
     * @param $uid        int      用户ID
     * @param $email      string   e-mail
     *
     * @return array
     */
    public function updateUserEmail($uid, $email){
        $params = array($uid, $email);
        return $this->_call('PF_User_UpdateEmailByUserID', $params, 1);
    }

    /**
     * 保存验证码到数据库
     *
     * @param $bind    string  绑定的主题，可以是手机号码，可以是E-mail地址
     * @param $code    string  验证码
     * @param $type    int     1： E-mail发送，   3：手机发送
     *
     * @return array
     */
    public function saveVerifyCode($bind, $code, $type){
        $time  = time();
        $param = array($bind, $code, $type, $time);
        return $this->_call('PF_Common_SaveVerifyCode', $param, 1);
    }
}


/**
 * 中云通数据库访问基础类
 * Author: Kenny.Hu
 * Date: 2016/2/1
 * Time: 8:46
 */
abstract class Zyt
{
    /* 数据库配模版
     *
     * $config = array(
     *     'DB_HOST' =>  '192.168.0.252',  // 服务器地址
     *     'DB_NAME' =>  'SSODB',          // 数据库名
     *     'DB_USER' =>  'sa',             // 用户名
     *     'DB_PWD'  =>  'sa',             // 密码
     *     'DB_PREFIX => 'sso_'            // 表前缀
     * );*/

    /** @var bool   调试模式 */
    public  $debug;

    /** @var array 语言包 */
    private $L;

    /**
     * 数据库连接配置
     */
    private $dbname;
    private $host;
    private $user;
    private $pwd;
    private $dbh;
    public  $prefix;

    // PDO操作实例
    private $PDOStatement;

    // 操作数据表
    private $table;

    // 操作数据表的别名
    private $alias;

    // 定义结果返回字段
    private $field = '*';

    // 查询条件
    private $where;

    // LIMIT 查询
    private $limit;

    // 链表查询
    private $join;

    // 查询结果排序
    private $order;

    // 表主键
    private $primary;

    // 待处理数据容器
    private $data;

    // 保存执行过的sql语句数组
    private $sqlStatements = array();

    // 事务指令数
    private $transTimes = 0;

    // 事务状态记录
    private $transFlag = 0;

    //----------------------------------------------------------------------------------------
    /**
     * 构造函数
     *
     * @param $configure array
     */
    public function __construct($configure){
        if( ! is_array($configure) || empty($configure) ) $this->__('CONFIGURE_ERROR');
        $this->language();   //加载语言

        $this->debug  = false;
        $this->dbname = $configure['DB_NAME'];
        $this->host   = $configure['DB_HOST'];
        $this->user   = $configure['DB_USER'];
        $this->pwd    = $configure['DB_PWD'];
        $this->prefix = $configure['DB_PREFIX'];
        $this->dbh    = $this->_connect();
    }

    /**
     * 选择数据表
     *
     * @param  $name string   数据名
     * @return $this stdClass
     */
    public function table($name){
        preg_match('/^\s*(\w+)\s+as\s+(\w+)\s*$/Ui', $name, $matches);
        if(!empty($matches)){
            $this->table = $this->prefix . $matches[1];
            $this->alias = $matches[2];
        }else{
            $this->table = $this->prefix . $name;
        }
        return $this;
    }

    /**
     * @param $fields
     * @return $this
     */
    public function field($fields){
        $this->field = $fields;
        return $this;
    }

    /**
     * 获取最后执行的SQL
     *
     * @return mixed
     */
    public function getLastSql(){
        $sql = end($this->sqlStatements);
        return false === $sql ? '' : $sql;
    }

    /**
     * 获取执行过的sql语句
     *
     * @return array
     */
    public function getAllSql(){
        return $this->sqlStatements;
    }

    /**
     * 定义Limit
     */
    public function limit(){
        $args = func_get_args();
        $len  = count($args);

        if($len === 1){
            $limit = $args[0];
            $tmp = explode(',', $limit);
            $len = count($tmp);
            if( $len === 1 && (int)$limit > 0 ){
                //TOP number
                $this->limit = trim($limit);
            }elseif($len > 1 && (int)$tmp[0] > 0 && (int)$tmp[1] > 0){
                //存在逗号隔开,多个逗号取前两个 , limit(3, 5)
                $offset = trim($tmp[0]);
                $rows   = trim($tmp[1]);
                $this->limit = array($offset, $rows);
            }
        }elseif($len > 1){
            $offset = (int)$args[0];
            $rows   = (int)$args[1];
            if( $offset >= 0 && $rows > 0 ){
                $this->limit = array($offset, $rows);
            }
        }
        return $this;
    }

    /**
     * 实现分页数据显示
     *
     * @param  $page
     * @param  $pageSize
     * @return $this
     */
    public function page($page, $pageSize){
        $page = (int)$page;  $pageSize = (int)$pageSize;
        if($page > 0 && $pageSize > 0){
            $offset = $pageSize * $page - $pageSize;
            $this->limit = array($offset, $pageSize);
        }
        return $this;
    }

    /**
     * 查询条件
     *
     * @param  $where
     * @param  $logic
     * @return $this
     */
    public function where($where, $logic = 'AND'){
        if( is_array($where) ){
            $string = '';
            $len = count($where);
            if( $len === 0 ){
                $this->where = '';
                return $this;
            }
            $n = 1;
            $processed = [];   //保存已处理过的数据
            foreach($where as $k => $v){
                if(in_array($k, $processed)) continue;   //跳过已处理
                if( is_array($v) ){

                    if( is_array($v[2]) ){
                        //字段A 与 字段B 捆绑一起，即是： (ColumnA = $valueA OR ColumnB = $valueB)
                        /*eg. $map['ColumnA'] = array('=', $valueA, array('ColumnB', 'OR'));
                          eg. $map['ColumnB'] = array('LIKE', $valueB , array('ColumnA', 'OR'));*/


                        $columnA = $k;
                        $columnB = $v[2][0];
                        $exp     = $v[2][1];

                        // >>> 检测 $where 数组中是否已存在与它绑定的对象
                        if($columnA != $columnB && isset($where[$columnB]) && $where[$columnB][2][0] === $columnA && $exp === $where[$columnB][2][1]){
                            $string .= "($columnA $v[0] '{$v[1]}' $exp $columnB {$where[$columnB][0]} '{$where[$columnB][1]}')";
                            array_push($processed, $columnB);  // 将columnB打入已处理过的数组，遍历时可以跳过处理。
                            $len -= 1;
                        }else{
                            $string .= $k . ' '  . $v[0] . ' ' . '\'' . $v[1] . '\'';
                        }
                    }else{
                        //eg. $map['a'] = array('<' , $value)
                        $string .= $k . ' '  . $v[0] . ' ' . '\'' . $v[1] . '\'';
                    }
                }else{
                    $string .= $k . ' = ' . '\'' . $v . '\'';
                }
                $string .= $n < $len ? ' ' . $logic . ' ' : '';
                $n++;
            }
            $this->where = $string;
        }else{
            $this->where = $where;
        }
        return $this;
    }

    /**
     * 链表查询
     *
     * @return $this
     */
    public function join(){
        $args   = func_get_args();
        $length = count($args);
        if($length === 1){
            $this->join .= ' JOIN ' . $this->_autoFillPrefix($args[0]);
        }elseif($length === 2){
            $this->join .= ' JOIN ' . $this->_autoFillPrefix(trim($args[0])) . ' ON ' . trim($args[1]);
        }elseif( $length > 2 ){
            $this->join .= ' JOIN ' . $this->_autoFillPrefix(trim($args[0])) . ' ON ' . trim($args[1]) . ' = ' . trim($args[2]);
        }
        return $this;
    }

    /**
     * 排序
     *
     * @param $order
     * @return $this
     */
    public function order($order){
        $this->order = $order;
        return $this;
    }

    /**
     * 返回select数组
     *
     * @return array
     */
    public function select(){
        $r = $this->_select();
        if(!$r) return [];
        $data = $r->fetchAll(PDO::FETCH_ASSOC);
        return false === $data ? [] : $data;
    }

    /**
     * 返回记录数
     *
     * @return int
     */
    public function count(){
        $this->field = 'count(*)';
        $this->order = '';
        $r = $this->_select();
        if(!$r) return 0;
        $data = (int)$r->fetch(PDO::FETCH_COLUMN);
        return false === $data ? 0 : $data;
    }

    /**
     * 返回一条数据
     *
     * @return array
     */
    public function find(){
        $r = $this->_select();
        $data = $r->fetch(PDO::FETCH_ASSOC);
        return false === $data ? [] : $data;
    }

    /**
     * 处理数据容器
     *
     * @param  $data
     * @return $this
     */
    public function data($data){
        if( is_array($data) && ! empty($data) ){
            foreach($data as $k => $v){
                $this->data[$k] = $v;
            }
        }
        return $this;
    }

    /**
     * 添加数据 | Insert
     *
     * @param array $data
     * @return bool|\PDOStatement
     */
    public function add($data = array()){
        if( !empty($data) ){
            $this->data($data);
        }
        if( empty($this->data) || empty($this->table) ) return false;

        $sql = 'INSERT INTO ' . $this->table;

        $fields = '(';
        $values = '(';
        $length = count($this->data);
        $n      = 1;
        foreach($this->data as $k => $v){
            $fields .= $k;
            if( is_array($v) && strtolower($v[0]) === 'exp' ){   //处理exp表达式
                $values .= $v[1];
            }else{
                $values .= '\''. $v . '\'';
            }
            if( $n < $length ){
                $fields .= ',';
                $values .= ',';
            }else{
                $fields .= ')';
                $values .= ')';
            }
            $n++;
        }
        $sql .= ' ' . $fields . ' VALUES ' . $values;
        $rows = $this->exec($sql);
        $this->data = array();

        return $rows;
    }

    /**
     * 获取最后插入的ID
     *
     * @return string
     */
    public function getLastInsertId(){
        return $this->dbh->lastInsertId();
    }

    /**
     * 更新数据 | UPDATE
     *
     * @param array $data
     * @return bool|\PDOStatement
     */
    public function save($data = array()){
        if( !empty($data) ){
            $this->data($data);
        }
        if( empty($this->data) || empty($this->table) ) return false;

        $sql = 'UPDATE ' . $this->table . ' SET ';

        $length = count($this->data);
        $n      = 1;
        foreach( $this->data as $k => $v ){

            if( is_array($v) && strtolower($v[0]) === 'exp' ){   //处理exp表达式
                $sql .= $k . ' = ' . $v[1];
            }else{
                $sql .= $k . '=\'' . $v . '\'';
            }
            $sql .= $n < $length ? ', ' : '';
            $n++;
        }

        if( !empty($this->where) ){
            $sql .= ' WHERE ' . $this->where;
        }

        $rows = $this->exec($sql);
        $this -> data = array();
        return $rows;
    }

    /**
     * 删除数据
     *
     * @return int
     */
    public function delete(){
        $table = $this->table;
        $sql = 'DELETE FROM ' . $table . ' where ' . $this->where;
        $r = $this->query($sql);
        if( false === $r ) return 0;
        return $r->rowCount();
    }

    /**
     * 开启事务
     */
    public function startTrans(){
        if ($this->transTimes == 0) {
            $this->dbh->beginTransaction();
            $this->transFlag = 1;
        }
        $this->transTimes++;
    }

    /**
     * 提交事务
     */
    public function commit(){
        if ($this->transTimes > 0) {
            try{
                $status = $this->transFlag;
                $this->transTimes = 0;
                $this->transFlag = 0;
                $result = $status === 1 ? $this->dbh->commit() : false;
                if( $result ){
                    return true;
                }else {
                    $this->dbh->rollBack();
                    return false;
                }
            }catch(\PDOException $e){
                $this->dbh->rollBack();
                $this->transTimes = 0;
                $this->transFlag = 0;
            }
        }
        return false;
    }

    /**
     * 执行SQL，并返回数据集
     *
     * @param  $sql
     * @return bool|\PDOStatement
     */
    public function query($sql){
        if( '' !== trim($sql) && ! is_null($sql) ){
            try{
                $result = $this->dbh->query($sql);
                $this->_init();
                $this->sqlStatements[] = $sql;
                return $result;
            }catch(\PDOException $e){
                if( $this->transTimes > 0 ){
                    $this->transFlag = 0;
                }
                $this->sqlStatements[] = $sql;
            }
        }
        $this->_init();
        return false;
    }

    /**
     * 执行SQL，返回影响记录数
     *
     * @param  $sql
     * @return bool|\PDOStatement
     */
    public function exec($sql){
        try{
            if( '' !== trim($sql) && ! is_null($sql) ){
                $result = $this->dbh->exec($sql);
                $this->_init();
                $this->sqlStatements[] = $sql;
                return $result;
            }
        }catch(\PDOException $e){
            if( $this->transTimes > 0 ){
                $this->transFlag = 0;
            }
            $this->sqlStatements[] = $sql;
        }
        $this->_init();
        return false;
    }

    /**
     * 调用存储过程
     *
     * @param $name
     * @param array $params
     * @param int $output_num 设置存储过程返回输出变量的个数
     * @return array
     */
    public function _call($name, $params = array(), $output_num = 0){
        if( !is_array($params) ) $this->__('PARAMS_ERROR');
        //生成SQL模版
        $sql = "exec $name";
        $sql = $this->_sql($sql, $params);
        $outputs = array();

        if( $output_num > 0 ){
            for($i=0; $i<$output_num; $i++){
                array_push($outputs, $i+1);
            }
        }
        $output_num = count($outputs);
        $sql = $output_num > 0 ? ( empty($params) ? $this->_sql($sql, $outputs, '') : $this->_sql($sql, $outputs, ',')) : $sql;
        $stmt = $this->dbh->prepare($sql);

        //绑定参数
        //初始化参数游标
        $i = 1;
        //绑定返回参数变量
        if( $output_num > 0 ){
            foreach($outputs as $k => $v){
                $stmt->bindParam($i, $outputs[$k], PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE);
                $i ++;
            }
        }

        //绑定参数变量
        if( !empty($params) ){
            foreach( $params as $k => $v ){
                $stmt->bindParam($i, $params[$k]);
                $i++;
            }
        }

        try{
            $result = $stmt->execute();
            $this->sqlStatements[] = $sql;
            if( ! $result ) {
                $this->__('SQL_STATEMENT_ERROR');
            }
            $stmt->setFetchMode(PDO::FETCH_ASSOC);
            if($this->transTimes == 0) {
                $fetches = $stmt->fetchAll();
                $result = array();
                if ($output_num > 0) {
                    $result['output'] = $outputs;
                    $result['list'] = $fetches;
                } else {
                    $result = $fetches;
                }
                return $result;
            }
        }catch(\PDOException $e){
            if( $this->transTimes > 0 ){
                $this->transFlag = 0;
            }
            $this->sqlStatements[] = $sql;
            return [];
        }
    }

    /**
     * 释放查询结果
     * @access public
     */
    public function free() {
        $this->PDOStatement = null;
    }

    /**
     * 创建数据库连接
     *
     * @return PDO
     */
    private function _connect(){
        $dbname = $this->dbname;
        $host = $this->host;
        $user = $this->user;
        $password = $this->pwd;
        $dsn = "sqlsrv:server=$host;database=$dbname";
        try {
            return new PDO($dsn, $user, $password, array(
                PDO::ATTR_ERRMODE    => PDO::ERRMODE_EXCEPTION,
            ));
        } catch ( PDOException $e) {
            $this->__('CONNECTION_FAILED');
        }
    }

    /**
     * 错误输出控制
     *
     * @param $msg
     * @param string $develop
     */
    private function __($msg, $develop = ''){
        $L = $this->L;
        exit( $develop === '' ? (isset($L[$msg]) ? $L[$msg] : $msg) : $develop );
    }

    /**
     * 语言包初始化
     */
    private function language(){
        $this->L = array(
            'CONFIGURE_ERROR' => '配置文件错误',
            'CONNECTION_FAILED' => '数据库连接错误',
            'PARAMS_ERROR' => '传入参数错误',
            'SQL_STATEMENT_ERROR' => 'SQL语句错误',
        );
    }

    /**
     * 拼接 SQL 模版
     *
     * @param $sql
     * @param $params
     * @param string $first
     * @return string
     */
    private function _sql($sql, $params, $first = ' '){
        $sql .= $first;
        foreach($params as $k => $v){
            $sql .=  '?,';
        }
        //去掉末尾的逗号
        if(substr($sql, -1, 1) == ','){
            $sql = substr($sql, 0, -1);
        }
        return $sql;
    }

    /**
     * 获取主键信息
     */
    private function _primary(){
        if(strlen($this->table)>0){
            $sql = "exec sp_pkeys {$this->table}";
            $query = $this->dbh->query($sql);
            $result = $query->fetch(PDO::FETCH_ASSOC);
            if($result){
                $this->primary = $result['COLUMN_NAME'];
            }
        }
    }

    /**
     * 自动填充表前缀
     *
     * @param $name
     * @return string
     */
    private function _autoFillPrefix($name){
        $name = trim($name);
        $prefix = $this->prefix;
        $len    = strlen($prefix);
        if( $len > 0 ){
            return $prefix === substr($name, 0, $len) ? $name : $this->prefix . $name;
        }
        return $name;
    }

    /**
     * SELECT 查询
     *
     * @return bool|\PDOStatement
     */
    private function _select(){
        //获取主键
        $this->_primary();

        $table = $this->table;
        $sql   = 'SELECT ';

        //存在LIMIT - 但是存入仅仅是一个数值,则直接 TOP
        if( ! empty($this->limit) && ! is_array($this->limit) ){
            //TOP
            if( !empty($this->limit) ){
                $sql .= 'TOP ' . $this->limit . ' ';
            }

            //FIELD
            $sql  .= $this->field;

            //FROM
            $sql  .= ' FROM ' . $table;

            //ALIAS
            if( !empty($this->alias) ){
                $sql .= ' AS ' . $this->alias;
            }

            //JOIN
            if( !empty($this->join) ){
                $sql .= $this->join;
            }

            //WHERE
            if( !empty($this->where)  ){
                $sql  .= ' WHERE ' . $this->where;
            }

            //ORDER
            if( !empty($this->order) )
                $sql .= ' ORDER BY ' . $this->order;
        }elseif( ! empty($this->limit) && is_array($this->limit) ){
            //存在LIMIT， 但存入的是两个数值， 需进行转换
            //定义一个临时随机别名
            $temp = $this->rand_name();
            $_NUM  = $this->rand_name();

            $sql .= '* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY ';

            if( !empty($this->order) ){
                $sql .= $this->order . ')';
            }else{
                $sql .= $this->primary . ')';
            }

            //FIELD
            $sql .= ' AS ' . $_NUM . ', ' . $this->field;

            //FROM
            $sql .= ' FROM '. $this->table . ' ';

            //ALIAS
            if( $this->alias ){
                $sql .= 'AS ' . $this->alias;
            }

            //JOIN
            if( $this->join ){
                $sql .= $this->join . ' ';
            }

            //WHERE
            if( $this->where ){
                $sql .= ' WHERE ' . $this->where . ' ';
            }
            $sql .= ') AS ' . $temp;
            $sql .= ' WHERE ' . $_NUM . ' BETWEEN ' . ($this->limit[0] + 1) . ' AND ' . ($this->limit[0] + $this->limit[1]);
        }else{
            //不需处理LIMIT
            //FIELD
            $sql  .= $this->field;

            //FROM
            $sql  .= ' FROM ' . $table;

            //ALIAS
            if( !empty($this->alias) ){
                $sql .= ' AS ' . $this->alias;
            }

            //JOIN
            if( !empty($this->join) ){
                $sql .= $this->join;
            }

            //WHERE
            if( !empty($this->where)  )
                $sql  .= ' WHERE ' . $this->where;

            //ORDER
            if( !empty($this->order) )
                $sql .= ' ORDER BY ' . $this->order;
        }
        $r = $this->query($sql);
        return $r;
    }

    //初始化
    private function _init(){
        $this->table   = null;
        $this->alias   = null;
        $this->field   = '*';
        $this->where   = null;
        $this->limit   = null;
        $this->join    = null;
        $this->order   = null;
        $this->primary = null;
        $this->data    = null;
    }

    //随机别名
    private function rand_name(){
        $pool = [
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        ];
        $name = '';
        $lenght = 4;
        for($i = 0; $i < $lenght; $i++){
            $r = rand(0, count($pool)-1);
            $name .= $pool[$r];
        }

        return $name;
    }
}